//
// Created by Administrator on 2021/8/27.
//

#ifndef OPENGLLEARN_APPLICATION_H
#define OPENGLLEARN_APPLICATION_H

#include "Common.h"

#include "glad/glad.h"
#include "GLFW/glfw3.h"

#include "Geometry/Vector3.h"
#include "Geometry/Matrix4x4.h"

#include <string>

// render-window app
// extends it and override some function
class Application
{
public:
    Application():
        width_(ScreenWidth), height_(ScreenHeight), title_("Application"), window_(nullptr) {}
    virtual ~Application() = default;

    void Run();

protected:
    virtual void OnCreate() {};
    virtual void OnDestroy() {};
    virtual void OnRender() {}
    virtual void OnResize(int w, int h)
    {
        glViewport(0, 0, w, h);
        OnSetProjectionMatrix(width_, height_, 0.1f, 1000.0f, 50.0f);
    };
    virtual void OnCursorPosChanged(float x, float y) { RotateCameraByCursor(x, y); }
    virtual void OnKeyPress(int key) { MoveCameraByKey(key); }
    virtual void OnKeyRelease(int key)
    {
        if(key == GLFW_KEY_ESCAPE)
        {
            glfwSetWindowShouldClose(window_, GLFW_TRUE);
        }
    }
    virtual void OnKeyRepeat(int key) { MoveCameraByKey(key); }

    virtual void OnSetCameraMatrix(const Vector3& pos, const Vector3& gaze, const Vector3& up);
    virtual void OnSetProjectionMatrix(int w, int h, float near, float far, float fov);

    Matrix4x4 cameraMatrix;
    Matrix4x4 projectMatrix;
    Vector3 camPos;
    Vector3 camForward;
    Vector3 camRight;
    Vector3 camUp;
    float lastCursorPosX;
    float lastCursorPosY;

private:
    static void glfwResize(GLFWwindow* window, int w, int h)
    {
        app_->OnResize(w, h);
    }
    static void glfwCursorPos(GLFWwindow* window, double x, double y)
    {
        app_->OnCursorPosChanged(static_cast<float>(x), static_cast<float>(y));
    }
    static void glfwKey(GLFWwindow* window, int key, int scancode, int action, int mods)
    {
        if(action == GLFW_PRESS)
        {
            app_->OnKeyPress(key);
        }
        else if(action == GLFW_RELEASE)
        {
            app_->OnKeyRelease(key);
        }
        else if(action == GLFW_REPEAT)
        {
            app_->OnKeyRepeat(key);
        }
    }
    static Application* app_;

    void MoveCameraByKey(int key);
    void RotateCameraByCursor(float x, float y);

    int width_;
    int height_;
    std::string title_;

    GLFWwindow* window_;
};

#endif //OPENGLLEARN_APPLICATION_H
